package com.lee.bgTask.queue;

import com.lee.bgTask.entity.TaskEntity;
import com.lee.bgTask.entity.impl.AbstractTaskEntity;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 平衡增长队列
 * @param <T>
 */
public class TaskQueue<T extends AbstractTaskEntity> {
    // 名称
    private String queueName;

    // 数据元素大小
    private int size;

    // 存储数据的数组
    private transient Object[] elementDatas;

    // 数组递增大小
    private final int GROW = 4;

    // 当前递增到第几个数
    private int growIndex;

    // 当前正在处理的对象在数组中下标
    private int workingIndex;

    public TaskQueue() {
        this.elementDatas = new Object[GROW];
    }

    /**
     *  清除全部
     */
    public synchronized void cleanAll() {
        size = 0;
        growIndex = 0;
        workingIndex = 0;
        this.elementDatas = new Object[GROW];
    }

    /**
     * 清除已完成
     */
    public synchronized void clean() {
        int unstartIndex = binarySearch();
        if (unstartIndex == -1) {
            cleanAll();
        } else {
            TaskQueue newQueue = new TaskQueue<>();
            for (int i = unstartIndex; i < size; i++) {
                newQueue.add(elementData(i));
            }
            copyof(newQueue);
        }
    }

    private void copyof(TaskQueue newQueue) {
        this.size = newQueue.size;
        this.growIndex = newQueue.growIndex;
        this.workingIndex = newQueue.workingIndex;
        this.elementDatas = newQueue.elementDatas;
    }

    /**
     * 二分发查找最小index
     *
     * @return
     */
    private int binarySearch() {
        int low = 0;
        int high = size - 1;
        while (low < high) {
            int middle = (low + high) / 2;
            T oldValue = elementData(middle);
            if (TaskEntity.Status.UNSTART.equals(oldValue.getStatus())) {
                high = middle;
            } else {
                low = middle + 1;
            }
        }
        return low == high ? low : -1;
    }

    /**
     * 获取指定下标元素（不改变元素状态)
     *
     * @param index
     * @return
     */
    public T show(int index) {
        return elementData(index);
    }

    /**
     * 获取元素开始处理
     *
     * @return
     */
    public T get() {
        for (int i = workingIndex; i < size; i++) {
            T oldValue = elementData(i);
            if (TaskEntity.Status.UNSTART.equals(oldValue.getStatus())) {
                oldValue.setStatus(TaskEntity.Status.PROCESSING);
                workingIndex++;
                return oldValue;
            }
        }
        return null;
    }

    /**
     * 处理完成元素状态提交
     *
     * @param taskEntity
     */
    public void submit(AbstractTaskEntity taskEntity) {
        int index = taskEntity.getIndex();
        T oldValue = elementData(index);
        if (TaskEntity.Status.PROCESSING.equals(oldValue.getStatus())) {
            oldValue.setStatus(TaskEntity.Status.FINSHED);
        } else {
            for (int i = 0; i < size; i++)
                if (taskEntity.equals(elementDatas[i])) {
                    oldValue = elementData(i);
                    if (TaskEntity.Status.PROCESSING.equals(oldValue.getStatus())) {
                        oldValue.setStatus(TaskEntity.Status.FINSHED);
                        return;
                    }
                }
        }
    }

    /**
     * 添加元素
     *
     * @param e
     * @return
     */
    public synchronized boolean add(T e) {
        ensureExplicitCapacity();
        e.setIndex(size);
        e.setStatus(TaskEntity.Status.UNSTART);
        elementDatas[size++] = e;
        return true;
    }

    /**
     * 显示所有元素
     *
     * @return
     */
    public List<T> listAll() {
        List<T> list = new ArrayList<>();
        Arrays.stream(elementDatas).forEach(t -> list.add((T) t));
        return list;
    }

    /**
     * 取消任务
     * @param o
     * @return
     */
    public boolean remove(Object o) {
        for (int index = 0; index < size; index++)
            if (o.equals(elementDatas[index])) {
                T oldValue = elementData(index);
                if (TaskEntity.Status.UNSTART.equals(oldValue.getStatus())) {
                    oldValue.setStatus(TaskEntity.Status.CANCEL);
                    return true;
                }
            }
        return false;
    }

    @SuppressWarnings("unchecked")
    T elementData(int index) {
        return (T) elementDatas[index];
    }

    private void ensureExplicitCapacity() {
        if (growIndex >= GROW) {
            elementDatas = Arrays.copyOf(elementDatas, size + GROW);
            growIndex = 0;
        }
        growIndex++;
    }

    public String getQueueName() {
        return queueName;
    }

    public void setQueueName(String queueName) {
        this.queueName = queueName;
    }

    public int getSize() {
        return size;
    }
}
